package com.iwdnb.gkgz.application.strategy;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.iwdnb.gkgz.application.model.request.AddStrategyTradeStockDataRequest;
import com.iwdnb.gkgz.common.enums.RangeTypeEnums;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StrategyTradeDTO;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.DateUtils;
import com.iwdnb.gkgz.common.utils.StockTrendUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;

@Service
@Slf4j
public class TrendThirteenLineBackStockBackValidateService extends CommmonStockBackValidateService {

    private static final String STATEGY_CODE = "trendThirteenBack";
    /**
     * 80天的毫秒数
     */
    private static final Long EIGHT_DAY_MILLIONS_SECOND = 80 * 86400 * 1000L;

    @Override
    public List<StrategyTradeDTO> queryBackValidateDateList(String code, String strategy,
        List<StockDayData> stockDayDataList,
        AddStrategyTradeStockDataRequest request) {
        int thirteenPeriod = 13;
        int twentyPeriod = 20;
        int thirtyPeriod = 30;
        int sixtyPeriod = 60;
        MovingAverage.calculateEMA(stockDayDataList, thirteenPeriod);
        MovingAverage.calculateEMA(stockDayDataList, 20);
        MovingAverage.calculateEMA(stockDayDataList, 30);
        MovingAverage.calculateEMA(stockDayDataList, 60);
        List<String> dateList = new ArrayList<>();
        List<StrategyTradeDTO> strategyTradeDTOList = new ArrayList<>();
        List<String> highDateStrList = stockRangeService.queryBollingAndHighPriceAndBiasDateList(code);
        List<Date> highDateList = highDateStrList.stream().map(highDateStr -> DateUtil.parseDate(highDateStr)).collect(
            Collectors.toList());
        for (int i = 0; i < stockDayDataList.size(); i++) {
            if (i < 60) {
                continue;
            }
            StockDayData data = stockDayDataList.get(i);
            String date = data.getDate();
            BigDecimal closePrice = data.getClosePrice();
            BigDecimal sxityAvg = getAveragePrice(data, sixtyPeriod);
            //在高点日期之后80天内都不能买
            if (isInHighDate(date, highDateList)) {
                continue;
            }
            //收盘价低于60日均线，跳过
            if (BigDecimalUtils.isLt(closePrice, sxityAvg)) {
                //log.debug("{}-{}收盘价高于20日均线，跳过", code, date);
                continue;
            }

            //过滤出买点日期
            StockDayData buyDayData = filterMatchDay(stockDayDataList, data);
            if (Objects.isNull(buyDayData)) {
                continue;
            }
            //涨停日比80个交易日前的最低价高60%，跳过
            BigDecimal beforeEightyLowPrice = StockUtils.getMinPriceBeforeDate(stockDayDataList, date, 80);
            BigDecimal beforeEightyLowRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                beforeEightyLowPrice);
            //log.debug("{}-{}-50日前的价差={}", code, date, beforeThirtyRate);
            if (BigDecimalUtils.isGt(beforeEightyLowRate, new BigDecimal(60))) {
                continue;
            }
            List<StockDayData> beforeTenDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date,
                10);
            StockDayData beforeTenData = beforeTenDayDatas.get(0);
            BigDecimal thirteenAvg = getAveragePrice(data, 30);
            BigDecimal beforeTenThirteenAvg = getAveragePrice(beforeTenData, 30);
            BigDecimal beforeTenRate = BigDecimalUtils.subStractAndDividePrecent(thirteenAvg, beforeTenThirteenAvg);
            //10日前的30日均价比当天低5%以内，跳过
            if (BigDecimalUtils.isLt(beforeTenRate, new BigDecimal(5))) {
                //log.debug("{}-{}10日前的20日均价比当天低3%以内，跳过", code, date);
                continue;
            }
            String twentyTrend = StockTrendUtils.getTrend(data, stockDayDataList, twentyPeriod);
            String thirtyTrend = StockTrendUtils.getTrend(data, stockDayDataList, thirtyPeriod);
            String sixtyTrend = StockTrendUtils.getTrend(data, stockDayDataList, sixtyPeriod);
            BigDecimal twentyAvg = getAveragePrice(data, twentyPeriod);
            BigDecimal thirtyAvg = getAveragePrice(data, thirtyPeriod);
            BigDecimal sixtyAvg = getAveragePrice(data, sixtyPeriod);
            //a.20、30、60日线均向上，且价格依次排布
            Boolean trendFlag = RangeTypeEnums.UP.equalsCode(twentyTrend) && RangeTypeEnums.UP.equalsCode(thirtyTrend)
                && RangeTypeEnums.UP.equalsCode(sixtyTrend)
                && BigDecimalUtils.isGe(twentyAvg, thirtyAvg) && BigDecimalUtils.isGe(thirtyAvg, sixtyAvg);
            if (!trendFlag) {
                //log.debug("{}-{}趋势向下，跳过", code, date);
                continue;
            }
            String buyDate = buyDayData.getDate();
            if (!dateList.contains(buyDate)) {
                dateList.add(buyDate);
                StrategyTradeDTO strategyTradeDTO = new StrategyTradeDTO();
                strategyTradeDTO.setCode(code);
                strategyTradeDTO.setUuid(STATEGY_CODE);
                strategyTradeDTO.setBuyDate(DateUtil.parseDate(buyDate));
                strategyTradeDTO.setBuyPrice(buyDayData.getClosePrice());
                strategyTradeDTO.setStatus("buy");
                strategyTradeDTOList.add(strategyTradeDTO);
                //BigDecimal profit = caculateAfterDaysProfit(backDayData.getDate(), stockDayDataList, 5);
                //log.debug("{}-{}买入,未来5天最高收益为:{}", code, buyDate, profit);
            }

        }
        return strategyTradeDTOList;
    }

    private boolean isInHighDate(String dateStr, List<Date> highDateList) {
        Date date = DateUtil.parseDate(dateStr);
        for (Date highDate : highDateList) {
            if (date.before(highDate)) {
                continue;
            }
            if (date.getTime() - highDate.getTime() < EIGHT_DAY_MILLIONS_SECOND) {
                return true;
            }
        }
        return false;
    }

    private StockDayData filterMatchDay(List<StockDayData> stockDayDataList, StockDayData data) {
        //去掉涨停板
        if (BigDecimalUtils.isGe(data.getRate(), new BigDecimal(9.7))) {
            return null;
        }
        //收盘价高于13日均线
        BigDecimal thirteenAvg = getAveragePrice(data, 13);
        if (BigDecimalUtils.isLt(data.getClosePrice(), thirteenAvg)) {
            //log.debug("{}-{}收盘价高于20日均线，跳过", code, date);
            return null;
        }
        //收盘价高于13日均线不超过3个点
        BigDecimal upAvgRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(), thirteenAvg);
        if (BigDecimalUtils.isGe(upAvgRate, new BigDecimal(3))) {
            return null;
        }
        //昨日收盘价在13日均线下方
        StockDayData yesterdayData = StockUtils.getYesterdayStockDayData(stockDayDataList, data.getDate());
        BigDecimal yesterdayClosePrice = yesterdayData.getClosePrice();
        BigDecimal yesterdayThirteenAvg = getAveragePrice(yesterdayData, 13);
        if (BigDecimalUtils.isGt(yesterdayClosePrice, yesterdayThirteenAvg)) {
            //log.debug("{}-{}前一天收盘价在20日均线下方，跳过", code, date);
            return null;
        }
        //前天收盘价在13日均线上方
        StockDayData twoDayData = StockUtils.getYesterdayStockDayData(stockDayDataList, yesterdayData.getDate());
        BigDecimal twoDayClosePrice = twoDayData.getClosePrice();
        BigDecimal twoDayThirteenAvg = getAveragePrice(yesterdayData, 13);
        if (BigDecimalUtils.isLt(twoDayClosePrice, twoDayThirteenAvg)) {
            //log.debug("{}-{}前一天收盘价在20日均线下方，跳过", code, date);
            return null;
        }

        return data;
    }

    @Override
    public void doBackValidate(String code, String strategy, List<StockDayData> stockDayDataList,
        List<StrategyTradeDTO> strategyTradeDTOList) {
        List<String> dateList = strategyTradeDTOList.stream().map(o -> DateUtil.formatDate(o.getBuyDate()))
            .collect(Collectors.toList());
        Date latestEndDate = strategyTradeDTOList.get(0).getBuyDate();
        latestEndDate = DateUtil.offsetDay(latestEndDate, -1);
        for (StrategyTradeDTO strategyTradeDTO : strategyTradeDTOList) {
            Date d = strategyTradeDTO.getBuyDate();
            if (DateUtils.beforeOrEquals(d, latestEndDate)) {
                log.debug("{}-{}在上一个周期内,忽略", code, d);
                continue;
            }
            getTradeInfo(code, stockDayDataList, strategyTradeDTO);
            latestEndDate = strategyTradeDTO.getSellDate();
        }
    }

    private void getTradeInfo(String code, List<StockDayData> stockDayDataList,
        StrategyTradeDTO strategyTradeDTO) {
        String date = DateUtil.formatDate(strategyTradeDTO.getBuyDate());
        //log.debug("{}-{}>>>>>股票信息:{}", code, date, strategyTradeDTO.getSummary());
        StockDayData buyDayData = StockUtils.getStockDayData(stockDayDataList, date);
        BigDecimal buyPrice = buyDayData.getClosePrice();
        List<StockDayData> subList = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date, 100);
        if (CollectionUtils.isEmpty(subList) || subList.size() < 10) {
            return;
        }
        int size = subList.size();
        BigDecimal closeRate = null;
        String sellDate = null;
        String info = "";
        BigDecimal sellPrice = null;
        BigDecimal rate = closeRate;
        int holdIndex = 0;
        BigDecimal rangeMaxPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeMaxRate = BigDecimal.ZERO;
        //跌破13日均线累计次数
        int falldownCount = 0;
        for (int i = 0; i < 100; i++) {
            if (i == size) {
                return;
            }
            StockDayData data = subList.get(i);
            StockDayData yesterdayDayData = i == 0 ? data : subList.get(i - 1);
            BigDecimal closePrice = data.getClosePrice();
            closeRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, buyPrice);
            sellDate = data.getDate();
            sellPrice = data.getClosePrice();
            holdIndex = i;
            BigDecimal upRate = BigDecimalUtils.subStractAndDividePrecent(data.getMaxPrice(), buyPrice);
            BigDecimal openRange = data.getOpenPrice().subtract(buyPrice);
            BigDecimal openRate = BigDecimalUtils.divideToPrecent(openRange, buyPrice);
            rangeMaxPrice = BigDecimalUtils.isGt(data.getMaxPrice(), rangeMaxPrice) ? data.getMaxPrice()
                : rangeMaxPrice;
            rangeMaxRate = BigDecimalUtils.subStractAndDividePrecent(rangeMaxPrice, buyPrice);
            BigDecimal realtimeRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(), buyPrice);
            rate = closeRate;
            String sellInfo = "收盘价卖出";

            // 跌5个点，跑路
            if (BigDecimalUtils.isLt(closeRate, new BigDecimal(-5))) {
                sellInfo = "跌了超过5个点,止损";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            if (BigDecimalUtils.isGe(realtimeRate, new BigDecimal(50))) {
                Integer yesterdayTradeNum = yesterdayDayData.getTradeNum() * 10;
                if (data.getTradeNum() >= yesterdayTradeNum) {
                    sellInfo = "出现十倍信号量,止盈";
                    log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo,
                        rate,
                        openRate,
                        closeRate, upRate);
                    info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                        + ",最高点收益=" + upRate;
                    break;
                }
            }
            //3天内的收益小于3个点，卖出
            if (i == 2 && BigDecimalUtils.isLt(rate, new BigDecimal(3))) {
                log.debug("第{}天-{}-{}-3天内的收益小于3个点，卖出，收益={}", (i + 1), code, data.getDate(), rate);
                info = "第" + (i + 1) + "天-3天内的收益小于3个点，卖出";
                break;
            }
            //回落超过3成
            if (i > 2
                && BigDecimalUtils.isGe(rangeMaxRate, new BigDecimal(5)) &&
                BigDecimalUtils.isGe(rangeMaxRate.multiply(new BigDecimal(0.7)), realtimeRate)) {
                sellInfo = "回落3成,卖出";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            BigDecimal thirteenAvg = getAveragePrice(data, 13);
            if (BigDecimalUtils.isGe(closePrice, thirteenAvg)) {
                falldownCount = 0;
            } else {
                falldownCount++;
            }
            if (falldownCount > 2) {
                sellInfo = "跌破13日均线,卖出";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            if (i == 99) {
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }

        }
        strategyTradeDTO.setHoldDay(holdIndex + 1);
        strategyTradeDTO.setSellDate(DateUtil.parseDate(sellDate));
        strategyTradeDTO.setSellPrice(sellPrice);
        strategyTradeDTO.setProfitRate(rate);
        strategyTradeDTO.setRangeMaxPrice(rangeMaxPrice);
        strategyTradeDTO.setRealtimeRate(BigDecimalUtils.subStractAndDividePrecent(rangeMaxPrice, buyPrice));
        strategyTradeDTO.setStrategyDescription(info);
        strategyTradeDTO.setStatus("sell");
        strategyTradeDTO.setWinFlag(BigDecimalUtils.isGt(rate, BigDecimal.ZERO) ? "y" : "n");
    }

}
